import abc
from collections.abc import Callable, Iterable, Iterator
from typing import Any, TypeVar

from .ioc import Ioc

AnyValue = TypeVar("AnyValue")
AnyInstance = TypeVar("AnyInstance")


class Component(abc.ABC):
    _ioc: Ioc

    def _iter_instance(self,
        query: type[AnyInstance] | Any, *,
        after: bool = True,
        strict: bool = False,
    ) -> Iterator[AnyInstance]:
        for instance in self._ioc.iter_instances(query):
            if after:
                self._ensure_after_instance(instance, strict=strict)
            yield instance

    def _get_instance(self,
        query: type[AnyInstance] | Any, *,
        singleton: bool = True,
        required: bool = True,
        after: bool = True,
        strict: bool = False,
    ) -> AnyInstance | None:
        instance = self._ioc.get_instance(query, singleton=singleton, required=required)
        if after:
            self._ensure_after_instance(instance, strict=strict)
        return instance

    def _scan_call_instances(self,
        query: type[AnyInstance] | Any,
        func: Callable[[AnyInstance], Any],
    ):
        return self._ioc.scan_call_instances(query, func)

    def _scan_agg_instances(self,
        query: type[AnyInstance] | Any,
        carry: AnyValue,
        func: Callable[[AnyInstance, AnyValue], AnyValue],
    ) -> AnyValue:
        return self._ioc.scan_agg_instances(query, carry, func)

    def _ensure_after_instance(self,
        instance: Iterable[AnyInstance], *,
        strict: bool = False,
    ):
        self._ioc.ensure_after_instance(instance, strict=strict)

    def _ensure_after_instances(self,
        instances: Iterable[Iterable[AnyInstance]], *,
        strict: bool = False,
    ):
        self._ioc.ensure_after_instances(instances, strict=strict)
